#include <jni.h>
#include <string>
#include <unistd.h>

#include "hashmap.h"

extern "C" {
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
#include "libavformat/avformat.h"
#include "libavcodec/mediacodec.h"
}

char *jstringToChar(JNIEnv *env, jstring string) {
    jsize charLen = env->GetStringLength(string);
    jsize utfLen = env->GetStringUTFLength(string);
    char *c = new char[utfLen + 1];
    env->GetStringUTFRegion(string, 0, charLen, c);
    return c;
}

extern "C"
JNIEXPORT jobject JNICALL
Java_com_zxhhyj_ffmpeg_tag_MediaTag_getMediaFormatStreamInfo(JNIEnv *env, jobject, jstring path) {
    const char *file_path = jstringToChar(env, path);
    AVFormatContext *formatCtx = avformat_alloc_context();
    if (avformat_open_input(&formatCtx, file_path, NULL, NULL) != 0) {
        delete[] file_path;
        avformat_close_input(&formatCtx);
        return nullptr;
    }
    if (avformat_find_stream_info(formatCtx, NULL) < 0) {
        delete[] file_path;
        avformat_close_input(&formatCtx);
        return nullptr;
    }
    av_dump_format(formatCtx, 0, file_path, 0);
    delete[] file_path;
    jlong duration = formatCtx->duration;
    jlong bitrate = formatCtx->bit_rate;
    int bitsPerRawSample = 0;
    jlong sampleRate = 0;
    int channels = 0;
    hashmap metadataMap = hashmap(env);
    AVDictionaryEntry *entry = nullptr;
    while ((entry = av_dict_get(formatCtx->metadata, "", entry, AV_DICT_IGNORE_SUFFIX))) {
        const char *key = entry->key;
        const char *value = entry->value;
        metadataMap.put(env->NewStringUTF(key), env->NewStringUTF(value));
    }
    for (int i = 0; i < formatCtx->nb_streams; i++) {
        if (formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            AVCodecParameters *codecParameters = formatCtx->streams[i]->codecpar;
            bitsPerRawSample = codecParameters->bits_per_raw_sample;
            sampleRate = codecParameters->sample_rate;
            channels = codecParameters->ch_layout.nb_channels;
            break;
        }
    }
    jbyteArray coverByteArray = NULL;
    for (int i = 0; i < formatCtx->nb_streams; ++i) {
        if (formatCtx->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC) {
            std::vector<uint8_t> coverVector;
            const AVPacket packet = formatCtx->streams[i]->attached_pic;
            coverVector.insert(coverVector.end(), packet.data, packet.data + packet.size);
            coverByteArray = env->NewByteArray(coverVector.size());
            env->SetByteArrayRegion(coverByteArray, 0, coverVector.size(),
                                    reinterpret_cast<jbyte *>(coverVector.data()));
        }
    }
    avformat_close_input(&formatCtx);
    jclass formatSteamInfoClass = env->FindClass("com/zxhhyj/ffmpeg/tag/FormatStreamInfo");
    jmethodID formatSteamConstructor = env->GetMethodID(formatSteamInfoClass, "<init>",
                                                        "([BJJIJILjava/util/Map;)V");
    return env->NewObject(formatSteamInfoClass, formatSteamConstructor,
                          coverByteArray,
                          duration,
                          bitrate,
                          bitsPerRawSample,
                          sampleRate,
                          channels,
                          metadataMap.getJavaObject());
}